import * as cc from "cc";
import { Queue } from "../../../Support/Share/Lib/Collections/Queue";
import { Log } from "../Log/Log";
import { AChannel, ChannelType } from "./AChannel";
import { ServiceType } from "./AService";
import { ErrorCore } from "./ErrorCore";
import { NetServices } from "./NetServices";
import { WService } from "./WService";
import { ETTask } from "../../../Support/Share/Lib/Async/ETTask";
import { BufferData, GetTypeOf } from "../../../Decorator/Decorator";
import { MessageSerializeHelper } from "./MessageSerializeHelper";

export class WChannel extends AChannel {

    private readonly Service: WService;

    private readonly webSocket: WebSocket;

    private readonly queue = new Queue<BufferData>();

    private isSending: boolean;

    private isConnected: boolean;

    constructor(id: number, webSocket: WebSocket, service: WService) {
        super();
        this.Id = id;
        this.webSocket = webSocket;
        this.Service = service;
        this.ChannelType = ChannelType.Accept;
    }

    public async initWebSocket() {
        let tcs = ETTask.Create<void>();
        this.webSocket.onopen = (evt: Event) => {
            // console.log(`websocket open : `);
            // console.log(evt);

            this.isConnected = true;
            tcs.SetResult();
        };
        this.webSocket.onmessage = (evt: MessageEvent) => {
            let data = evt.data;

            // console.log(`websocket message : `, data.toString());
            // console.log(data);

            this.OnServerMessageRead(data);
        };
        this.webSocket.onerror = (evt: Event) => {
            let err = evt;

            Log.Error(`websocket error : `, err);

            this.OnError(ErrorCore.ERR_WebsocketError);
        };
        this.webSocket.onclose = (evt: CloseEvent) => {
            let code = evt.code;
            let reason = evt.reason;

            // console.log(`websocket close : `, code, reason);

            this.OnError(ErrorCore.ERR_WebsocketError);
        };
        return tcs;
    }

    private async OnServerMessageRead(data: BufferData) {
        if (cc.sys.isBrowser) {
            this.OnServerMeesageReadBrowser(data as Blob);
        } else {
            this.OnServerMeesageReadNative(data as ArrayBuffer);
        }
    }

    private async OnServerMeesageReadBrowser(data: Blob) {
        let tsc = ETTask.Create<Uint8Array>();
        let reader = new FileReader();
        reader.onload = (evt) => {
            try {
                let resultBuf = reader.result as ArrayBuffer;
                let buffer = new Uint8Array(resultBuf);
                tsc.SetResult(buffer);
            } catch (error) {
                tsc.SetException(error);
            }
        }
        reader.readAsArrayBuffer(data);
        let buffer = await tsc;
        this.OnRead(buffer);
    }

    private async OnServerMeesageReadNative(data: ArrayBuffer) {
        let buffer = new Uint8Array(data);
        this.OnRead(buffer);
    }

    public Send(actorId: number, stream: BufferData) {
        this.queue.Enqueue(stream);

        if (this.isConnected) {
            this.StartSend();
        }
    }

    public async StartSend() {
        if (this.IsDisposed) {
            return;
        }
        try {
            if (this.isSending) {
                return;
            }
            this.isSending = true;
            while (true) {
                if (this.queue.Count == 0) {
                    this.isSending = false;
                    return;
                }
                let data = this.queue.Dequeue();
                try {
                    this.webSocket.send(data);
                    if (this.IsDisposed) {
                        return;
                    }
                } catch (e) {
                    Log.Error(e);
                    this.OnError(ErrorCore.ERR_WebsocketSendError);
                    return;
                }
            }
        } catch (e) {
            Log.Error(e);
        }
    }

    private OnRead(data: Uint8Array) {
        try {
            let channelId = this.Id;
            let message = null;
            let actorId = 0;
            // console.log(`WChannel OnRead`);
            switch (this.Service.ServiceType) {
                case ServiceType.Outer:
                    {
                        let { opcode, serializeMessage } = MessageSerializeHelper.StreamToMessage(data);

                        message = serializeMessage;

                        console.log(`opcode = ${opcode}`);
                        console.log(`message = ${GetTypeOf(message)}`);
                        console.log(message);
                        console.log(typeof message);
                        break;
                    }
            }

            NetServices.Instance.OnRead(this.Service.Id, channelId, actorId, message);
        } catch (e) {
            Log.Error(`${this.RemoteAddress} {memoryStream.Length} ${e}`);
            // 出现任何消息解析异常都要断开Session，防止客户端伪造消息
            this.OnError(ErrorCore.ERR_PacketParserError);
        }
    }

    private OnError(error: number) {
        Log.Info(`WChannel error: ${error} ${this.RemoteAddress}`);
        let channelId = this.Id;
        this.Service.Remove(channelId);
        NetServices.Instance.OnError(this.Service.Id, channelId, error);
    }

    public Dispose(): void {
    }

}